//
// Created by RandBII on 2020/12/27.
//


#include "AudioGLRender.h"

AudioGLRender *AudioGLRender::mPInstance = nullptr;
std::mutex  AudioGLRender::mMutex;

AudioGLRender *AudioGLRender::GetInstance() {
    if (mPInstance == nullptr) {
        std::unique_lock<std::mutex> lock(mMutex);
        if (mPInstance == nullptr) {
            mPInstance = new AudioGLRender();
        }
    }
    return mPInstance;
}

void AudioGLRender::ReleaseInstance() {
    std::unique_lock<std::mutex> lock(mMutex);
    if (mPInstance) {
        delete mPInstance;
        mPInstance = nullptr;
    }
}

void AudioGLRender::OnSurfaceCreated() {
    if (mProgramObj) return;
//     3.0中 uniform 被替换了 、
// todo 随后验证
    char vShaderStr[] =
            "#version 300 es\n"
            "layout(location = 0) in vec4 a_position;\n"
            "layout(location = 1) in vec2 a_texCoord;\n"
            "uniform mat4 u_MVPMatrix;\n"
            "out vec2 v_texCoord;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = u_MVPMatrix * a_position;\n"
            "    v_texCoord = a_texCoord;\n"
            "    gl_PointSize = 4.0f;\n"
            "}";
//   TODO   3.0中 版本中 还有 uniform  关键字吗？待验证
    char fShaderStr[] =
            "#version 300 es                                     \n"
            "precision mediump float;                            \n"
            "in vec2 v_texCoord;                                 \n"
            "layout(location = 0) out vec4 outColor;             \n"
            "uniform float drawType;                             \n"
            "void main()                                         \n"
            "{                                                   \n"
            "  if(drawType == 1.0)                               \n"
            "  {                                                 \n"
            "      outColor = vec4(1.5 - v_texCoord.y, 0.3, 0.3, 1.0); \n"
            "  }                                                 \n"
            "  else if(drawType == 2.0)                          \n"
            "  {                                                 \n"
            "      outColor = vec4(1.0, 1.0, 1.0, 1.0);          \n"
            "  }                                                 \n"
            "  else if(drawType == 3.0)                          \n"
            "  {                                                 \n"
            "      outColor = vec4(0.3, 0.3, 0.3, 1.0);          \n"
            "  }                                                 \n"
            "}                                                   \n";

    mProgramObj = GLHelper::CreateProgram(vShaderStr, fShaderStr);
    if (mProgramObj == GL_NONE) {
        LOG_E("AudioGLRender::OnSurfaceCreated mProgramObj is null ");
    }

//投影
    glm::mat4 projection = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, 0.1f, 100.0f);

//    观察点
    glm::mat4 view = glm::lookAt(
            glm::vec3(0, 0, 4),
            glm::vec3(0, 0, 0),
            glm::vec3(0, 1, 0)
    );
    glm::mat4 model = glm::mat4(1.0f);
    model = glm::scale(model, glm::vec3(1.0f, 1.0f, 1.0f));
    model = glm::rotate(model, 0.0f, glm::vec3(1.0f, 0.0f, 0.0f));
    model = glm::rotate(model, 0.0f, glm::vec3(0.0f, 1.0f, 0.0f));
    model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f));
//    mMvpMatrix 变换矩阵
    mMVPMatrix = projection * view * model;
}

void AudioGLRender::OnSurfaceChanged(int width, int height) {
    glClearColor(1.0f, 1.0f, 1.0f, 1.0);
    glViewport(0, 0, width, height);
}


void AudioGLRender::SetTouchLoc(float touchX, float touchY) {

}

void AudioGLRender::UpdateAudioFrame(AudioFrame *audioFrame) {
    if (audioFrame) {
        std::unique_lock<std::mutex> lock(mMutex);
        if (mPAudioFrameBuffer && mPAudioFrameBuffer->dataSize != audioFrame->dataSize) {
            if (mPAudioFrameBuffer) {
                delete mPAudioFrameBuffer;
                mPAudioFrameBuffer = nullptr;
            }
            if (mTextureCoords) {
                delete mTextureCoords;
                mTextureCoords = nullptr;
            }
            if (mVerticesCoords) {
                delete mVerticesCoords;
                mVerticesCoords = nullptr;
            }
        }
        if (mPAudioFrameBuffer == nullptr) {
            mPAudioFrameBuffer = new AudioFrame(audioFrame->data, audioFrame->dataSize);
            mRenderDataSize = mPAudioFrameBuffer->dataSize / RESAMPLE_LEVEL;
            mVerticesCoords = new glm::vec3[mRenderDataSize * 6];
            mTextureCoords = new glm::vec2[mRenderDataSize * 6];
        } else {
            memcpy(mPAudioFrameBuffer->data, audioFrame->data, audioFrame->dataSize);
        }
        lock.unlock();
    }

}

void AudioGLRender::Init() {
    mVaoId = GL_NONE;
    mTextureCoords = nullptr;
    mVerticesCoords = nullptr;
    memset(mVboIds, 0, sizeof(GLuint) * 2);
    mPAudioFrameBuffer = nullptr;
}

void AudioGLRender::UnInit() {
    if (mPAudioFrameBuffer) {
        delete mPAudioFrameBuffer;
        mPAudioFrameBuffer = nullptr;
    }
    if (mTextureCoords) {
        delete mTextureCoords;
        mTextureCoords = nullptr;
    }
    if (mVerticesCoords) {
        delete mVerticesCoords;
        mVerticesCoords = nullptr;
    }
}

AudioGLRender::AudioGLRender() {
    Init();
}

AudioGLRender::~AudioGLRender() {
    UnInit();
}

void AudioGLRender::UpdateMesh() {

    float dy = 0.25f / MAX_AUDIO_LEVEL;
    float dx = 1.0f / mRenderDataSize;

    for (int i = 0; i < mRenderDataSize; ++i) {
        int index = i * RESAMPLE_LEVEL;
        auto *pValue = (short *) (mPAudioFrameBuffer->data + index);
        float y = *pValue * dy;
        y = y < 0 ? y : -y;
        glm::vec2 p1(i * dx, 0 + 1.0f);
        glm::vec2 p2(i * dx, 0 + 1.0f);
        glm::vec2 p3((i + 1) * dx, y + 1.0f);
        glm::vec2 p4((i + 1) * dx, 0 + 1.0f);

        mTextureCoords[i * 6 + 0] = p1;
        mTextureCoords[i * 6 + 1] = p2;
        mTextureCoords[i * 6 + 2] = p4;
        mTextureCoords[i * 6 + 3] = p4;
        mTextureCoords[i * 6 + 4] = p2;
        mTextureCoords[i * 6 + 5] = p3;

        mVerticesCoords[i * 6 + 0] = GLHelper::textCoordToVertexCoord(p1);
        mVerticesCoords[i * 6 + 1] = GLHelper::textCoordToVertexCoord(p2);
        mVerticesCoords[i * 6 + 2] = GLHelper::textCoordToVertexCoord(p4);
        mVerticesCoords[i * 6 + 3] = GLHelper::textCoordToVertexCoord(p4);
        mVerticesCoords[i * 6 + 4] = GLHelper::textCoordToVertexCoord(p2);
        mVerticesCoords[i * 6 + 5] = GLHelper::textCoordToVertexCoord(p3);

    }

}

void AudioGLRender::OnDrawFrame() {

    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    std::unique_lock<std::mutex> lock(mMutex);
    if (mProgramObj == GL_NONE || mPAudioFrameBuffer == nullptr) return;
    UpdateMesh();
    lock.unlock();

    if (mVboIds[0] == 0) {
        glGenBuffers(2, mVboIds);

        glBindBuffer(GL_ARRAY_BUFFER, mVboIds[0]);
        glBufferData(GL_ARRAY_BUFFER,
                     sizeof(GLfloat) * mRenderDataSize * 6 * 3,
                     mVerticesCoords,
                     GL_DYNAMIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, mVboIds[1]);
        glBufferData(GL_ARRAY_BUFFER,
                     sizeof(GLfloat) * mRenderDataSize * 6 * 2,
                     (void *) mTextureCoords,
                     GL_DYNAMIC_DRAW);
    } else {

        glBindBuffer(GL_ARRAY_BUFFER, mVboIds[0]);
        glBufferSubData(GL_ARRAY_BUFFER,
                        0,
                        sizeof(GLfloat) * mRenderDataSize * 6 * 3,
                        mVerticesCoords);

        glBindBuffer(GL_ARRAY_BUFFER, mVboIds[1]);
        glBufferSubData(GL_ARRAY_BUFFER,
                        0,
                        sizeof(GLfloat) * mRenderDataSize * 6 * 3,
                        mTextureCoords);

    }

    if (mVaoId == GL_NONE) {
        glGenVertexArrays(1, &mVaoId);
        glBindVertexArray(mVaoId);

        glBindBuffer(GL_ARRAY_BUFFER, mVboIds[0]);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
                              3 * sizeof(GLfloat), nullptr);

        glBindBuffer(GL_ARRAY_BUFFER, mVboIds[1]);
        glEnableVertexAttribArray(1);

        //todo
    }
    glUseProgram(mProgramObj);
    glBindVertexArray(mVaoId);
    GLHelper::SetMat4(mProgramObj, "u_MVPMatrix", mMVPMatrix);
    GLHelper::SetFloat(mProgramObj, "drawType", 1.0f);
    glDrawArrays(GL_TRIANGLES, 0, mRenderDataSize * 6);
    GLHelper::SetFloat(mProgramObj, "drawType", 2.0f);
    glDrawArrays(GL_LINES, 0, mRenderDataSize * 6);
}

void AudioGLRender::UpdateMVPMatrix(int angelX, int angelY, int scaleX, int scaleY) {

}


